/**
 * 
 */
package com.itboye.allshengmedical.sci;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.R.bool;
import android.R.integer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.BaseColumns;
import android.text.style.BulletSpan;
import android.util.Log;

import com.allsheng.serial.SerialPort;
import com.example.allshengmedical.experimentrun.activity.MyLog;
import com.example.allshengmedical.util.ByExceptionUtil;
import com.itboye.allshengmedical.sci.cmd.req.BaseCmd;
import com.itboye.allshengmedical.sci.cmd.req.CmdFactory;
import com.itboye.allshengmedical.sci.cmd.req.CmdType;
import com.itboye.allshengmedical.sci.cmd.req.CommandQueue;
import com.itboye.allshengmedical.sci.cmd.req.IReqCommand;
import com.itboye.allshengmedical.sci.cmd.resp.RespDataHelper;
import com.itboye.allshengmedical.sci.packet.resp.BaseRespPacket;
import com.itboye.allshengmedical.sci.packet.resp.RespPacketParser;

/**
 * 
 * @modify 2017-05-27 重构了寻找有效数据串的算法
 * 
 * @author 贝贝 346551990@qq.com
 * @date 2016-12-8
 */
public class DefaultSerialControl implements ISerialControl {

	final String tag = "serial";

	/**
	 * 指令队列
	 */
	private CommandQueue queueInstance;

	private SerialPort mSerialPort;// 串口对象
	private OutputStream mOutputStream;// 串口输出流
	private InputStream mInputStream;// 串口输入流

	private WriteThread mWriteThread = null;// 写入数据线程
	private ReadThread mReadThread = null;// 读取数据线程
	private TimeOutThread timeOutThread = null;// 超时判断线程

	private String lastError;// 最近一次错误信息

	private String sPort;// 串口号
	private int iBaudRate;// 波特率

	private byte[] buffer;

	static DefaultSerialControl instance = null;

	private Object readLock;// 读锁
	private Object writeLock;// 写锁
	private Object timeOutLock;// 超时锁

	private Handler msgHandler;
	private Boolean isSerailReady = false;// 串口是否打开成功
	private StringBuilder builder = new StringBuilder();//已读取的字符串
	int index;

	@Override
	public void setHandler(Handler handler) {
		this.msgHandler = handler;
	}

	public static synchronized DefaultSerialControl getInstance() {

		if (instance == null) {
			instance = new DefaultSerialControl(SerialConfig.SerialPost,
					SerialConfig.BaudRate);
		}

		return instance;
	}

	public DefaultSerialControl(String sPort, int iBaudRate) {
		this.sPort = sPort;
		this.iBaudRate = iBaudRate;
		this.queueInstance = CommandQueue.getInstance();
		this.readLock = new Object();
		this.writeLock = new Object();
		this.timeOutLock = new Object();
		this.msgHandler = new Handler();
	}

	public DefaultSerialControl(String sPort, String sBaudRate) {
		this(sPort, Integer.parseInt(sBaudRate));
	}

	/**
	 * 添加指令
	 **/
	public void addCommand(IReqCommand cmd) {
		this.queueInstance.pushCommand(cmd);
	}

	/**
	 * 添加指令
	 */
	public void addCommand(CmdType type, String todo, int temp) {
		this.queueInstance.pushCommand(CmdFactory.create(type, todo, temp));
	}

	public void addCommand(CmdType type, String todo, int temp, int temp2,
			int temp3, int temp4) {
		this.queueInstance.pushCommand(CmdFactory.create(type, todo, temp,
				temp2, temp3, temp4));
	}

	@Override
	public String getLastError() {
		return lastError;
	}

	/**
	 * 控制器线程是否正在运行中
	 */
	@Override
	public Boolean isRunning() {
		return (this.isReadThreadRunning() && this.isWriteThreadRunning());
	}

	public Boolean isReadThreadRunning() {
		return this.mReadThread != null && mReadThread.isAlive();
	}

	public Boolean isWriteThreadRunning() {
		return this.mWriteThread != null && mWriteThread.isAlive();
	}

	public Boolean isTimeOutThreadRunning() {
		return this.timeOutThread != null && timeOutThread.isAlive();
	}

	@Override
	public Boolean safeCheck() {

		if (this.mSerialPort == null) {
			this.lastError += "串口未打开;";
		}

		if (this.mInputStream == null) {
			this.lastError += "输入流未初始化;";
		}

		if (this.mOutputStream == null) {
			this.lastError += "输入流未初始化";
		}

		if (!this.isReadThreadRunning()) {
			this.lastError += "读线程未运行;";
		}

		if (!this.isWriteThreadRunning()) {
			this.lastError += "写线程未运行;";
		}

		return this.lastError.length() == 0;
	}

	/**
	 * 可以多次调用start
	 */
	@Override
	public void start() {

		Log.d(tag, "=准备启动读写线程=");
		if (!isReadThreadRunning()) {
			startReadThread();

		} else {
			Log.d(tag, "=读线程已经在运行=");
		}

		if (!this.isWriteThreadRunning()) {
			startWriteThread();
		} else {
			Log.d(tag, "=写线程已经在运行=");
		}
		if (!this.isTimeOutThreadRunning()) {
			startTimeOutThread();
		} else {
			Log.d(tag, "=判断超时线程已经在运行=");
		}
		Log.d(tag, "=成功启动读写线程=");
	}

	@Override
	public void pause() {

		Log.d(tag, "=准备关闭读线程=");
		stopReadThread();
		Log.d(tag, "=成功关闭读线程=");
		Log.d(tag, "=准备关闭写线程=");
		stopWriteThread();
		Log.d(tag, "=成功关闭写线程=");
		Log.d(tag, "=准备关闭超时线程=");
		stopTimeOutThread();
		Log.d(tag, "=成功关闭超时线程=");
	}

	/*
	 * 初始化
	 */
	@Override
	public void init() {
		if (this.openComm()) {
			isSerailReady = true;
			this.start();
		} else {
			isSerailReady = false;
		}

	}

	@Override
	public void release() {

		this.pause();
		this.closeComm();
	}

	/**
	 * 字节数组转换为十六进制字符串
	 * @param src
	 * @return
	 */
	private String bytesToHexString(byte[] src) {
		StringBuilder stringBuilder = new StringBuilder("");
		if (src == null || src.length <= 0) {
			return null;
		}

		for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}

		return stringBuilder.toString();
	}
	
	/**
	 * 判断是否追加字符串
	 * @return
	 */
	private boolean isAppend() {
		boolean isAppend = false;
		int start = builder.toString().indexOf("5aa5");
		if (builder.length() >= 6 && (start + builder.length()) >= subMethod()) {
			isAppend = true;
		}
		return isAppend;

	}

	private int subMethod() {
		String s = builder.substring(builder.toString().indexOf("5aa5") + 4,
				builder.toString().indexOf("5aa5") + 6);
		return Integer.parseInt(s, 16) * 2 + 14;

	}

	/**
	 * 向观察对象发送读取成功的数据
	 * 
	 * @param readData
	 */
	synchronized void readSuccess(byte[] readData, int size) {
		// void readSuccess(String readData, int size) {
		// 要读取到一个完整的响应包
		String hex = bytesToHexString(readData);
		builder.append(hex.substring(0,size * 2));
		Log.d(tag, "读入数据串= " + builder.toString());
		String resp = RespDataHelper.getInstance().push(builder.toString());
		int len = RespDataHelper.getInstance().getBuilderString().length();
		System.out.println("len= "+ len);
//		System.out.println("[flag_of_find_length]"+(isFindStartFlag == true?"true":"false")+"已读入字符串 = " + RespDataHelper.getInstance().getBuilderString());
		
		if (len > 0) {
			if(resp.length() > 0){
				builder.setLength(0);
				RespDataHelper.getInstance().clearBuilderString();
				index = 0;
				Log.d(tag, "可识别数据串= " + resp);
				BaseRespPacket packet = RespPacketParser.parse(resp);
				queueInstance.removeSentCommand(packet.getCmdType());
				Message msg = new Message();
				msg.what = FindPacketMessage;
				if(!packet.isValidPack()){
					Log.d(tag, "数据串校验失败 = " + packet.toString());
				}else{
					Log.d(tag, "数据串校验成功 = " + packet.toString());

					Log.d(tag, "【success】可识别数据串= " + resp);
					// 这三句可以传递数据
					Bundle data = new Bundle();
					data.putString("cmdType", packet.getCmdType().getValue());
					data.putSerializable("data", packet.getContent());
					data.putSerializable("packet", packet);
					msg.setData(data);
					if (msgHandler != null) {
						msgHandler.sendMessage(msg);
					}
				}
			}else{
				Log.d(tag, "[wait]当前字符串不是可识别的" + builder.toString());
			}
		}else{
			Log.d(tag, "[clear]当前字符串不是可识别的" + builder.toString());
			builder.setLength(0);
		}
		
	}

	/**
	 * mSerialPort是否初始化过
	 */
	@Override
	public Boolean isOpened() {
		return mSerialPort != null;
	}

	/**
	 * 打开串口
	 */
	public boolean openComm() {
			
		Log.d(tag, "=准备打开串口。。。。=");
		try {
			mSerialPort = new SerialPort(new File(sPort), iBaudRate, 0);
			mOutputStream = mSerialPort.getOutputStream();
			mInputStream = mSerialPort.getInputStream();
			buffer = new byte[256];
			Log.d(tag, "=成功打开串口。。。。");
			return true;

		} catch (SecurityException e) {
			ByExceptionUtil.log(e);
			Log.d(tag, "=打开串口安全异常=");
			lastError = "打开串口安全异常:" + e.getMessage();

		} catch (IOException e) {
			ByExceptionUtil.log(e);
			Log.d(tag, "=打开串口IO异常=");
			lastError = "打开串口IO异常:" + e.getMessage();
		}

		return false;
	}

	private void clearBuffer(){
		for(int i=0;i<buffer.length ;i++){
			buffer[i] = 0;
		}
	}
	
	/*
	 * 关闭串口
	 */
	public void closeComm() {

		Log.d(tag, "=准备关闭串口=");
		try {

			synchronized (readLock) {
				if (mInputStream != null) {
					mInputStream.close();
					mInputStream = null;
				}
			}

			synchronized (writeLock) {
				if (mOutputStream != null) {
					mOutputStream.flush();
					mOutputStream.close();
					mOutputStream = null;
				}
			}

			if (mSerialPort != null) {
				mSerialPort.close();
				mSerialPort = null;
			}

			System.gc();

		} catch (IOException e) {
			ByExceptionUtil.log(e);

			lastError = "关闭串口异常:" + e.getMessage();
		}

	}

	/**
	 * 开启读线程,如果已开启则不做任何操作
	 */
	private void startReadThread() {
		if (isSerailReady && mReadThread == null) {
			mReadThread = new ReadThread();
			mReadThread.start();
		}
	}

	/**
	 * 
	 * 关闭读线程,如果已关闭则不做任何操作
	 */
	private void stopReadThread() {
		synchronized (readLock) {

			if (mReadThread != null) {
				mReadThread.interrupt();
				mReadThread = null;
			}

		}
	}

	/**
	 * 开启写线程,如果已开启则不做任何操作
	 */
	private void startWriteThread() {
		if (isSerailReady && mWriteThread == null) {
			mWriteThread = new WriteThread();
			mWriteThread.start();
		}
	}

	public void sendCmd(byte cmd[]) {
		if (null != mOutputStream) {
			Log.e("hu...mOutputStream", cmd.length + "");
			try {
				mOutputStream.write(cmd);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			Log.e("hu...mOutputStream", "null");
		}
	}

	/**
	 * 
	 * 关闭写线程,如果已关闭则不做任何操作
	 */
	private void stopWriteThread() {

		synchronized (writeLock) {
			if (mWriteThread != null) {
				mWriteThread.interrupt();
				mWriteThread = null;
			}
		}
	}

	private void stopTimeOutThread() {
		if (timeOutThread != null) {
			timeOutThread.interrupt();
			timeOutThread = null;
		}
	}

	private void startTimeOutThread() {
		if (isSerailReady && timeOutThread == null) {
			timeOutThread = new TimeOutThread();
			timeOutThread.start();
		}
	}

	/**
	 * 超时
	 */
	public void CheckTimeOutCommand() {
		long time = System.currentTimeMillis();
		for (int i = 0; i < queueInstance.getSendedcmdStack().size(); i++) {
			BaseCmd baseCmd = queueInstance.getSendedcmdStack().get(i);
			if (baseCmd.isTimeOut(time, baseCmd.getCmdType())) {
				// TODO
				Message msg = new Message();
				msg.what = SCTimeoutMessage;
				queueInstance.removeSentCommand(baseCmd.getCmdType());
				Bundle data = new Bundle();
				data.putString("cmdType", baseCmd.getCmdType().getValue());
				Log.e("hu_sc_timeout", "有一条超时指令类型"
						+ baseCmd.getCmdType().toDesc());
//				MyLog.e("hu_sc_timeout", "有一条超时指令类型"
//						+ baseCmd.getCmdType().toDesc());
				msg.setData(data);
				if (msgHandler != null) {
					msgHandler.sendMessage(msg);
				}
			}
		}
	}

	private class TimeOutThread extends Thread {
		@Override
		public void run() {
			super.run();
			while (!isInterrupted()) {
				try {
					CheckTimeOutCommand();
					Log.d("sc_timeout", "已发出指令队列长度"
							+ queueInstance.getSendedcmdStack().size());
					// 10ms
					Thread.sleep(SerialConfig.TimeOutInterval);
				} catch (InterruptedException ex) {
					Thread.interrupted();
					return;
				} catch (Exception e) {
					ByExceptionUtil.log(e);
					lastError = e.getMessage();
				}
			}
		}
	}

	/**
	 * 写线程
	 * 
	 * @author 贝贝 346551990@qq.com
	 * @date 2016-12-8
	 */
	private class WriteThread extends Thread {
		@Override
		public void run() {
			super.run();
			while (!isInterrupted()) {
				try {

					synchronized (writeLock) {

						if (mOutputStream == null)
							break;

						BaseCmd cmd = (BaseCmd) queueInstance.pollCommand();
						if (cmd != null) {
							Log.d(tag, "=向串口写入指令("
									+ cmd.getClass().getSimpleName() + ")");
							byte[] data = cmd.convertBytes();
							Log.d(tag, "=十六进制数据=" + bytesToHexString(data));
							// 取出一个命令，向串口进行写入操作
							mOutputStream.write(data);
							// ((BaseCmd)cmd)
							cmd.setSendTimeStamp(System.currentTimeMillis());
							queueInstance.pushSendedCommand(cmd);

						} else {
							// Log.d("wait_serail_writing", "等待上位机命令中...");
						}
					}

					// 重置
					Thread.sleep(SerialConfig.WriteInterval);
				} catch (InterruptedException ex) {
					Thread.interrupted();
					return;
				} catch (Exception e) {
					ByExceptionUtil.log(e);
					lastError = e.getMessage();
				}
			}
		}
	}

	/**
	 * 读数据线程
	 * 
	 * @author 贝贝 346551990@qq.com
	 * @date 2016-12-8
	 */
	private class ReadThread extends Thread {
		@Override
		public void run() {
			super.run();
			while (!isInterrupted()) {
				try {
					boolean needSleep = false;
					synchronized (readLock) {
						if (mInputStream == null)
							break;
						if (0 == mInputStream.available()) {
							// Log.e("wait_serail_reading", "等待下位机返回数据...");
							needSleep = true;

						} else {
							int size = mInputStream.read(buffer);
							if (size > 0) {
								Log.d(tag, "读取到" + size + "字节的数据:"
										+ bytesToHexString(buffer));
								readSuccess(buffer, size);
								clearBuffer();
							}
						}
					}

					if (needSleep && queueInstance.sendedcmdStack.size() == 0) {
						Thread.sleep(SerialConfig.ReadInterval);
					}

				} catch (InterruptedException e) {
					Thread.interrupted();
					ByExceptionUtil.log(e);
					return;
				} catch (Throwable e) {
					ByExceptionUtil.log(e);
					lastError = e.getMessage();
				}
			}

		}
	}

}
